package top.zhangjianyong.blog.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.zhangjianyong.blog.cache.UserCache;
import top.zhangjianyong.blog.cache.VCodeCache;
import top.zhangjianyong.blog.entity.dto.MenuDTO;
import top.zhangjianyong.blog.entity.dto.UserDTO;
import top.zhangjianyong.blog.entity.dto.UserRoleDTO;
import top.zhangjianyong.blog.entity.po.File;
import top.zhangjianyong.blog.entity.po.Role;
import top.zhangjianyong.blog.entity.po.User;
import top.zhangjianyong.blog.entity.po.UserRole;
import top.zhangjianyong.blog.enums.FileType;
import top.zhangjianyong.blog.mapper.UserMapper;
import top.zhangjianyong.blog.params.user.*;
import top.zhangjianyong.blog.record.DelAndAdd;
import top.zhangjianyong.blog.response.ApiResponse;
import top.zhangjianyong.blog.service.*;
import top.zhangjianyong.blog.utils.EntityUtil;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import static top.zhangjianyong.blog.enums.RespCode.*;

/**
 * 用户Service
 *
 * @author zhangjianyong
 * @since 2024/7/19 下午1:29
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserRoleService userRoleService;
    private final FileServiceImpl fileService;
    private final MenuService menuService;
    private final BlogService blogService;
    private final BlogCommentService blogCommentService;
    private final VisitLogService visitLogService;

    public UserServiceImpl(
        @Lazy UserRoleService userRoleService,
        @Lazy FileServiceImpl fileService,
        @Lazy MenuService menuService,
        @Lazy BlogService blogService,
        @Lazy BlogCommentService blogCommentService,
        @Lazy VisitLogService visitLogService) {
        this.userRoleService = userRoleService;
        this.fileService = fileService;
        this.menuService = menuService;
        this.blogService = blogService;
        this.blogCommentService = blogCommentService;
        this.visitLogService = visitLogService;
    }

    @Override
    public ApiResponse login(LoginParam param) {
        // 查询用户
        User user = queryByUsername(param.username());
        if (null == user) {
            return ApiResponse.fail(USER_NOT_FUND);
        }
        // 验证密码
        String userPassword = user.getPassword();
        if (!BCrypt.checkpw(param.password(), userPassword)) {
            return ApiResponse.fail(USER_PASSWORD_ERROR);
        }
        /*登陆成功保存用户信息到Session*/
        UserCache.user(user);
        /*保存权限信息*/
        List<Role> roles = userRoleService.listByUserId(user.getId());
        List<String> roleKeys = roles.stream().map(Role::getRoleKey).toList();
        UserCache.roles(roleKeys);
        return ApiResponse.success(user);
    }

    @Override
    public ApiResponse passwordChange(PasswordChangeParam param) {

        User user = getById(UserCache.userId());
        if (!BCrypt.checkpw(param.oldPassword(), user.getPassword())) {
            return ApiResponse.fail(USER_PASSWORD_ERROR);
        }
        String password = BCrypt.hashpw(param.newPassword(), BCrypt.gensalt());
        user.setPassword(password);
        if (changePassword(user)) {
            return ApiResponse.success(user);
        }
        return ApiResponse.fail(USER_CHANGE_PASSWORD_ERROR);
    }

    @Override
    public ApiResponse page(UserPageParam param) {
        Page<User> page =
            lambdaQuery()
                .like(StrUtil.isNotBlank(param.username()), User::getUsername, param.username())
                .page(new Page<>(param.pageNum(), param.pageSize()));
        return ApiResponse.success(page);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResponse remove(UserDeleteParam param) {
        boolean remove = lambdaUpdate().eq(User::getId, param.id()).remove();
        if (!remove) return ApiResponse.fail(USER_DELETE_ERROR);
        return userRoleService.removeByUser(param.id())
            ? ApiResponse.success()
            : ApiResponse.fail(USER_CLEAR_ROLE_ERROR);
    }

    @Override
    public ApiResponse saveOrUpdate(UserSaveOrUpdateParam param) {
        User user = new User();
        BeanUtils.copyProperties(param, user);
        boolean result = null == user.getId() ? save(user) : updateById(user);
        return result ? ApiResponse.success() : ApiResponse.fail(USER_SAVE_OR_UPDATE_ERROR);
    }

    @Override
    public ApiResponse roles(UserRolesParam param) {
        return ApiResponse.success(getRolesByUserId(param.id()));
    }

    @Override
    public ApiResponse update(UserRolesUpdateParam param) {
        // 查询已有的角色
        UserRoleDTO userRoleDTO = getRolesByUserId(param.id());
        // 删除的角色和新增的角色
        DelAndAdd<Long> delAndAdd = EntityUtil.getDelAndAdd(userRoleDTO.getRoles(), param.roles());
        // 删除和新增操作
        boolean remove = userRoleService.remove(param.id(), delAndAdd.del());
        boolean save = userRoleService.save(param.id(), delAndAdd.add());
        return remove && save ? ApiResponse.success() : ApiResponse.fail(USER_ROLE_UPDATE_ERROR);
    }

    @Override
    public ApiResponse avatarUpload(AvatarUploadParam param) {
        try {
            File file = fileService.save(param.file(), FileType.BASE.getType());
            // 保存头像
            return lambdaUpdate()
                .eq(User::getId, UserCache.userId())
                .set(User::getAvatar, file.getId())
                .update()
                ? ApiResponse.success(file)
                : ApiResponse.fail(USER_AVATAR_SAVE_ERROR);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return ApiResponse.fail(USER_AVATAR_UPLOAD_ERROR);
        }
    }

    @Override
    public ApiResponse passwordReset(PasswordResetParam param) {
        User user = queryByUsername(param.username());
        String email = user.getEmail();
        if (VCodeCache.verifyCodeNotPass(email, param.code())) {
            return ApiResponse.fail(USER_V_CODE_NOT_PASS);
        }
        String password = BCrypt.hashpw(param.password(), BCrypt.gensalt());
        user.setPassword(password);
        if (changePassword(user)) {
            return ApiResponse.success(user);
        }
        return ApiResponse.fail(USER_CHANGE_PASSWORD_ERROR);
    }

    @Override
    public ApiResponse update(UserUpdateParam param) {
        User user = new User();
        BeanUtils.copyProperties(param, user);
        user.setId(UserCache.userId());
        return updateById(user) ? ApiResponse.success() : ApiResponse.fail(USER_UPDATE_ERROR);
    }

    @Override
    public ApiResponse gitUpdate(UserGitUpdateParam param) {
        User user = new User();
        BeanUtils.copyProperties(param, user);
        user.setId(UserCache.userId());
        return updateById(user) ? ApiResponse.success() : ApiResponse.fail(USER_GIT_UPDATE_ERROR);
    }

    @Override
    public ApiResponse logout(LogoutParam param) {
        UserCache.remove();
        return ApiResponse.success();
    }

    @Override
    public ApiResponse menu(UserMenuParam param) {
        List<MenuDTO> menuDTOS = menuService.menu(UserCache.userId());
        return ApiResponse.success(menuDTOS);
    }

    @Override
    public ApiResponse get(UserGetParam param) {
        return ApiResponse.success(getById(param.id()));
    }

    private UserRoleDTO getRolesByUserId(Long userId) {
        MPJLambdaWrapper<User> wrapper =
            new MPJLambdaWrapper<User>()
                .select(User::getId)
                .selectCollection(
                    UserRole.class, UserRoleDTO::getRoles, map -> map.result(UserRole::getRoleId))
                .leftJoin(UserRole.class, UserRole::getUserId, User::getId)
                .eq(User::getId, userId);
        return baseMapper.selectJoinOne(UserRoleDTO.class, wrapper);
    }

    @Override
    public User queryByUsername(String username) {
        return lambdaQuery().eq(User::getUsername, username).one();
    }

    private boolean changePassword(User user) {
        return lambdaUpdate()
            .eq(User::getId, user.getId())
            .set(User::getPassword, user.getPassword())
            .update();
    }

    @Override
    public ApiResponse getUserInfo(UserGetParam param) {
        User user = getById(param.id());
        if (user == null) {
            return ApiResponse.fail(USER_NOT_FUND);
        }

        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(user, userDTO);

        // 博客数量
        long blogCount = blogService.countByUser(userDTO.getId());
        userDTO.setBlogCount(blogCount);

        // 评论数量
        long commentCount = blogCommentService.countByUser(userDTO.getId());
        userDTO.setCommentCount(commentCount);

        // 访问数量
        long visitCount = visitLogService.count();
        userDTO.setVisitCount(visitCount);

        // 计算运行时间天数，当前时间减去创建时间
        LocalDateTime createTime = userDTO.getCreateTime();
        if (createTime != null) {
            LocalDateTime now = LocalDateTime.now();
            long runningDays = ChronoUnit.DAYS.between(createTime, now);
            userDTO.setRunningDays(runningDays);
        } else {
            userDTO.setRunningDays(0);
        }

        // 最后更新时间
        LocalDateTime lastBlogPushTime = blogService.getLastBlogPushTime(userDTO.getId());
        userDTO.setLastPushBlogTime(lastBlogPushTime);

        return ApiResponse.success(userDTO);
    }

    @Override
    public ApiResponse check(UserCheckParam param) {
        return ApiResponse.success();
    }
}
